/**
 * Copyright 2010 Victor Egea Hernando
 *
 * RockScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * RockScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with RockScript.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "pjit_codegen_x86.h"

#include "../../pjit_block.h"
#include "../../pjit_buffer.h"

#include <iostream>

#if PICOJIT_TARGET == 1 || PICOJIT_TARGET == 2

namespace pjit{

pjit_codegen_x86::pjit_codegen_x86( void ){
	_nbytes_pushed = _nbytes_sp = 0;
}

pjit_codegen_x86::~pjit_codegen_x86(){

}

void pjit_codegen_x86::save_stack_conntext( pjit_asm_x86 &asm_x86 ){
	if( !_nbytes_pushed ){
		// save context [enter]:
		asm_x86.push_reg32( REBP );
		asm_x86.mov_reg32_to_reg32( RESP, REBP );
	}
}

void pjit_codegen_x86::skip_n_bytes( pjit_asm_x86 &asm_x86, uint32 bytes ){
	asm_x86.block().skip( bytes );
	asm_x86.block().asm_os() << "   ;-- [gap] skipping ";
	asm_x86.block().asm_os() << bytes << " bytes --" << std::endl;
}

void pjit_codegen_x86::compile( pjit_block &block, pjit_buffer &buffer ){
	// reserve memory
	pjit_asm_x86 asm_x86( block, buffer.size() * 3 );
	// initial and current offset
	uint32 ioffset = block.offset();
	// skip (n) bytes to generate the opcodes
	// which will perform the stack reservation
	skip_n_bytes( asm_x86, 6 );
	// translate LIR quads
	pjit_buffer::quad_it quad = buffer.begin_quad();
	// just an example
	_nbytes_sp = 4;
	// translate
	while( quad != buffer.end_quad() ){
		switch( (*quad).op ){
			case pjit_buffer::pusharg32:{
				save_stack_conntext( asm_x86 );
				// push argument
				asm_x86.push_imm32( (*quad).rs1.vuint32, PRINT_UINT );
				_nbytes_pushed += 4;
				break;
			}case pjit_buffer::pushargptr:{
				save_stack_conntext( asm_x86 );
				// push argument
				uint32 dir = reinterpret_cast<uint32>( (*quad).rs1.pvoid );
				asm_x86.push_imm32( dir, PRINT_HEX );
				_nbytes_pushed += 4;
				break;
			}case pjit_buffer::pushargregref:{
				/*
				 * JUST A SIMPLE DRAFT
				 */
				save_stack_conntext( asm_x86 );
				// push argument
				//asm_x86.push_imm32( (*quad).rs1.vuint32, PRINT_HEX );
				asm_x86.mov_reg32_to_reg32( REBP, REBX );
				asm_x86.add_imm32_to_reg32( REBX, (*quad).rs1.vuint32, PRINT_HEX );
				asm_x86.push_reg32( REBX );
				_nbytes_pushed += 4;
				break;
			}case pjit_buffer::call:{
				// call [(*quad).rs1.pvoid]
				uint32 dir = reinterpret_cast<uint32>( (*quad).rs1.pvoid );
				asm_x86.mov_imm32_to_reg32( REAX, dir, PRINT_HEX );
				asm_x86.far_call_reg32( REAX );
				/* restore context [leave]:
				   pop %ebp
				   mov  %ebp, %esp */
				asm_x86.leave();
				_nbytes_pushed = 0;
				break;
			}case pjit_buffer::ret:{
				// leave the context as it was before
				asm_x86.add_imm32_to_reg32( RESP, _nbytes_sp, PRINT_UINT );
				asm_x86.ret();
				break;
			}case pjit_buffer::label:{
				if( !(*quad).rs1.vuint32 && block.back_asm() ){
					block.asm_os() << "   main:" << std::endl;
				}else if( block.back_asm() ){
					block.asm_os() << "   label_";
					block.asm_os() << (*quad).rs1.vuint32 << ":" << std::endl;
				}
				break;
			}
		}
		// next quad
		quad = buffer.next_quad();
	}

	// CHANGE CODE THROUGH THE BLOCK
	block.asm_os() << std::endl;
	block.asm_os() << "   ;## adding opcodes through the block ##" << std::endl;
	// go back and reserve the stack to allocate the needed variables
	block.go_to( ioffset );
	asm_x86.sub_imm32_to_reg32( RESP, _nbytes_sp, PRINT_UINT );
}

};

#endif // PICOJIT_TARGET == 1 || PICOJIT_TARGET == 2
